/*

Gamebots UT Copyright (c) 2002, Andrew N. Marshal, Gal Kaminka
Gamebots UT 2003/2004 port Copyright (c) 2005
       Joe Manojlovich, Tim Garwood, Jessica Bayliss
Gamebots Pogamut derivation Copyright (c) 2007, Michal Bida
	- derived mainly from 2002 Marshal, Kaminka version

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software must also be in compliance with the Epic Games Inc. license for mods which states the following: "Your mods must be distributed solely for free, period. Neither you, nor any other person or party, may sell them to anyone, commercially exploit them in any way, or charge anyone for receiving or using them without prior written consent of Epic Games Inc. You may exchange them at no charge among other end-users and distribute them to others over the Internet, on magazine cover disks, or otherwise for free." Please see http://www.epicgames.com/ut2k4_eula.html for more information.

*/


class ControlConnection extends GBClientClass;

//Parent server
var ControlServer Parent;

var config float UpdateTime;

var() class<pickup> tempInventory; //for inventory spawns

var() class<actor> tempActorClass; //for actor spawns

//Accepted connection to a socket
event Accepted()
{
	log("Control Connection established.");

	if(bDebug)
		log("Accepted ControlConnection");

	if (bNewProtocol)
		SendLine("HELLO CONTROL SERVER");
	else
		SendGameInfo();

    if (BotDeathMatch(Level.Game).bPasswordProtected)
		gotoState('checkingPassword','Waiting');
	else
		gotoState('running','Waiting');
}

//Connection closed at remote end
event Closed()
{
	Destroy();
}

event Destroyed()
{
	SendLine("FIN");
}

function PostBeginPlay()
{
	Parent = ControlServer(Owner);
	if(bDebug)
		log("Spawned ControlConnection");
}

//Handles commands for ControlServer
function ProcessAction(string cmdType)
{
	if(bDebug)
		log("comandType:"@cmdType);

	if (IsInState('checkingPassword'))
	{
		switch(cmdType)
		{
			case "PASSWORD":
				ReceivedPassword();
			break;
			case "READY":
				ReceivedReady();
			break;
		}
	}
	else
	{
		switch(cmdType)
		{
			case "ADDBOT":
				ReceivedAddBot();
			break;
			case "ADDINV":
				ReceivedAddInv();
			break;
			case "CHANGEMAP":
				ReceivedChangeMap();
			break;
			case "CHANGETEAM":
				ReceivedChangeTeam();
			break;
			case "CHATRR":
				ReceivedChAtrr();
			break;
			case "CLEAR":
				ReceivedClear();
			case "CONF":
				ReceivedConf();
			break;
			case "CONFGAME":
				ReceivedConfGame();
			break;
			case "CONSOLE":
				ReceivedConsole();
			break;
			case "ENDPLRS":
				gotoState('running','Waiting');
			break;
			case "GETINVS":
				ExportInventory();
			break;
			case "GETMAPS":
				ReceivedGetMaps();
			break;
			case "GETNAVS":
				ExportNavPoints();
			break;
			case "GETPLRS":
				ExportPlayers();
			break;
			case "PAUSE":
				ReceivedPause();
			break;
			case "PING":
				SendLine("PONG");
			break;
			case "QUIT":
				Destroy();
			break;
			case "KICK":
				ReceivedKick();
			break;
			case "READY":
				ReceivedReady();
			break;
			case "REC":
				ReceivedRec();
			break;
			case "RESPAWN":
				ReceivedRespawn();
			break;
			case "SETGAMESPEED":
				ReceivedSetGameSpeed();
			break;
			case "SETLOCK":
				ReceivedSetLock();
			break;
			case "SETPASS":
				ReceivedSetPass();
			break;
			case "SPAWNACTOR":
				ReceivedSpawnActor();
			break;
			case "STARTPLRS":
				ReceivedStartPlrs();
			break;
			case "STOPREC":
				ReceivedStopRec();
			break;
		}//end switch
	}//end if
}

function ReceivedAddBot()
{
	local string target;
	local vector vector1;
	local rotator rotator1;
	local float floatNumber;
	local int intNumber;

	target = GetArgVal("Name");
	ParseVector(vector1,"StartLocation");
	ParseRot(rotator1,"StartRotation");
	floatNumber = float(GetArgVal("Skill"));
	intNumber = int(GetArgVal("Team"));
	if( Level.Game.isA('BotDeathMatch'))
	{
		BotDeathMatch(Level.Game).AddEpicBot(target, intNumber, vector1, rotator1, floatNumber);
	}
}

function ReceivedAddInv()
{
	local string target, string1;
	local Controller C;

	target = GetArgVal("Id");

	if (target == "") {
		return;
	}
	string1 = GetArgVal("Class");
	string1 = "class'"$string1$"'";
	setPropertyText("tempInventory",string1);
	//log("Added inventory will be "$tempInventory);
	if (tempInventory != None)
		for(C = Level.ControllerList; C != None; C = C.NextController )
		{
			if( (C$C.PlayerReplicationInfo.PlayerID) == target )
			{
				RemoteBot(C).SpawnInventory(tempInventory);

			}

		}

}

function ReceivedChangeMap()
{
	local string target;
	local bool bResult;
	local GBClientClass G;
	local Controller C;
	local int i;

	target = GetArgVal("MapName");
	if (target != "")
	{
		//Check if the map exists in current map list
		bResult = false;
		for (i=0; i < BotDeathMatch(Level.Game).Maps.Length; i++ )
		{
			if (target==BotDeathMatch(Level.Game).Maps[i])
			{
			 	bResult = true;
			 	break;
			}
		}
    	//Change just when the map is in MapList
		if (bResult)
		{
						//SendLine("MAPCHANGE"$ib$as$"MapName"$ib$target$ae);
			for(C = Level.ControllerList; C != None; C = C.NextController )
			{
				if (C.isA('RemoteBot'))
				{
					RemoteBot(C).myConnection.SendLine("MAPCHANGE"$ib$as$"MapName"$ib$target$ae);
					RemoteBot(C).myConnection.SendLine("FIN");
				}

			}
			for (G = Parent.ChildList; G != None; G = G.Next )
			{
				G.SendLine("MAPCHANGE"$ib$as$"MapName"$ib$target$ae);
				G.SendLine("FIN");
			}
    		//We refuse another connections when changing map
			BotDeathMatch(Level.Game).theBotServer.bClosed = true;
			BotDeathMatch(Level.Game).theBotServer.Close();

			BotDeathMatch(Level.Game).theControlServer.bClosed = true;
			BotDeathMatch(Level.Game).theControlServer.Close();

			Level.ServerTravel(target,false);
		}
	}
}

function ReceivedChangeTeam()
{
	local string temp, target;
	local Controller C;

	if (!Level.Game.IsA('BotTeamGame'))
		return;

	target = GetArgVal("Id");

	if (target == "")
		return;

	for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( (C$C.PlayerReplicationInfo.PlayerID) == target )
		{
			if (C.IsA('RemoteBot'))
				break;
			else
				return;
		}

	}

	if (C != none)
	{

		temp = GetArgVal("Team");
		if (temp != "")
		{
	    	if (BotTeamGame(Level.Game).ChangeTeam(C, int(temp), true))
			{
				RemoteBot(C).myConnection.SendLine("TEAMCHANGE"$ib$as$"Success"$ib$"True"$ae$ib$as$"DesiredTeam"$ib$temp$ae);
				GlobalSendLine("TEAMCHANGE"$ib$as$"Id"$ib$C$C.PlayerReplicationInfo.PlayerID$ae$ib$as$"Success"$ib$"True"$ae$ib$as$"DesiredTeam"$ib$temp$ae,true,false);
			}
			else
			{
			    RemoteBot(C).myConnection.SendLine("TEAMCHANGE"$ib$as$"Success"$ib$"False"$ae$ib$as$"DesiredTeam"$ib$temp$ae);
				GlobalSendLine("TEAMCHANGE"$ib$as$"Id"$ib$C$C.PlayerReplicationInfo.PlayerID$ae$ib$as$"Success"$ib$"False"$ae$ib$as$"DesiredTeam"$ib$temp$ae,true,false);
			}
	    }
	}
}

// this will do a dirty clear, sometimes bots remain on the server after disconnect
// this delete all actors that are pawns or controllers on the server
//
function ReceivedClear()
{
	local GBxPawn P;
	local RemoteBot R;
	local BotConnection C;
	//local Controller C;

    foreach AllActors (class 'BotAPI.BotConnection', C, )
	{
		C.Destroy();
	}

	foreach AllActors (class 'BotAPI.RemoteBot', R, )
	{
		R.Destroy();
	}

	foreach AllActors (class 'BotAPI.GBxPawn', P, )
	{
		P.Destroy();
	}


}

function ReceivedChAtrr()
{
	local string target;
	local Controller C;

	target = GetArgVal("Id");
	if (target == "")
		return;

	for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( (C$C.PlayerReplicationInfo.PlayerID) == target )
		{
			break;
		}

	}
	if (C == none)
		return;

	target = GetArgVal("Health");
	if (target != "")
	{
		RemoteBot(C).SetHealth(int(target));
	}

	target = GetArgVal("Adrenaline");
	if (target != "")
	{
		RemoteBot(C).SetAdrenaline(int(target));
	}

}

function ReceivedConf()
{
	local string target, string1;
	local float floatNumber;
	local Controller C;
	local int i;

	target = GetArgVal("Id");
	if (target == "")
		return;

	for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( (C$C.PlayerReplicationInfo.PlayerID) == target )
		{
			break;
		}

	}
	if (C == none)
		return;

	if (GetArgVal("AutoTrace") != "")
		RemoteBot(C).bAutoTrace = bool(GetArgVal("AutoTrace"));
	if (GetArgVal("ManualSpawn") != "")
		RemoteBot(C).bAutoSpawn = !bool(GetArgVal("ManualSpawn"));
	if (GetArgVal("ShowFocalPoint")!="")
		RemoteBot(C).bShowFocalPoint = bool(GetArgVal("ShowFocalPoint"));
	if (GetArgVal("ShowDebug")!="")
		RemoteBot(C).bDebug = bool(GetArgVal("ShowDebug"));
	if (GetArgVal("Name") != "")
	{
		string1 = GetArgVal("Name");
    	C.PlayerReplicationInfo.PlayerName = string1;
		Level.Game.changeName( C, string1, true );
	}
	if (GetArgVal("Invulnerable") != "")
		RemoteBot(C).bGodMode = bool(GetArgVal("Invulnerable"));

	if (GetArgVal("DrawTraceLines") != "")
	{
		RemoteBot(C).bDrawTraceLines = bool(GetArgVal("DrawTraceLines"));

		if (!RemoteBot(C).bDrawTraceLines)
		{
			//Need to change the settings of already spawned rays
			for (i = 0; i < RemoteBot(C).myConnection.CustomRayList.Length;i++)
			{
				if (RemoteBot(C).myConnection.CustomRayList[i].VisualizerMiss != none)
				{
                 	RemoteBot(C).myConnection.CustomRayList[i].VisualizerMiss.bHidden = true;
				}
				if (RemoteBot(C).myConnection.CustomRayList[i].VisualizerHit != none)
				{
                	RemoteBot(C).myConnection.CustomRayList[i].VisualizerHit.bHidden = true;
				}
			}
		}

	}
	if (GetArgVal("VisionTime") != "")
	{
		floatNumber = float(GetArgVal("VisionTime"));
		if ((floatNumber >= 0.1) && (floatNumber <= 2))
			RemoteBot(C).myConnection.visionTime = floatNumber;
	}
	if (GetArgVal("SynchronousOff") != "")
	{
		RemoteBot(C).myConnection.bSynchronousMessagesOff = bool(GetArgVal("SynchronousOff"));
	}
	SendNotifyConf(RemoteBot(C));

}

function ReceivedConfGame()
{
	local int intNumber;
	local float floatNumber;

	if (GetArgVal("DamageModifier") != "")
	{
		floatNumber = float(GetArgVal("DamageModifier"));
		if ((floatNumber > 0) && (floatNumber <= 10) )
			BotDeathMatch(Level.Game).DamageModifier = floatNumber;
	}

	if (GetArgVal("MaxSkill") != "")
	{
		floatNumber = float(GetArgVal("MaxSkill"));
		if ((floatNumber > 0) && (floatNumber <= 7) )
			BotDeathMatch(Level.Game).MaxSkill = floatNumber;
	}
	    /*
	if (GetArgVal("WeaponStay") != "")
	{
		Level.Game.bWeaponStay = bool(GetArgVal("WeaponStay"));
	}
	if (GetArgVal("WeaponThrowing") != "")
	{
		Level.Game.bAllowWeaponThrowing = bool(GetArgVal("WeaponThrowing"));
	}
	if (GetArgVal("GoalScore") != "")
	{
		intNumber = int(GetArgVal("GoalScore"));
		if (intNumber >= 1)
			Level.Game.GoalScore = intNumber;
	}
	if (GetArgVal("TimeLimit") != "")
	{
		intNumber = int(GetArgVal("TimeLimit"));
		if (intNumber >= 1)
			Level.Game.TimeLimit = intNumber;
	}
	if (GetArgVal("MaxLives") != "")
	{
		intNumber = int(GetArgVal("MaxLives"));
		if (intNumber >= 1)
			Level.Game.MaxLives = intNumber;
	}     */

}

function ReceivedConsole()
{
	local string target;

	target = GetArgVal("Command");

	ConsoleCommand(target, True);

}

function ReceivedGetMaps()
{
	local int i;

	SendLine("SMAP");
	for (i=0; i < BotDeathMatch(Level.Game).Maps.Length; i++ ) {
		SendLine("IMAP"$ib$as$"Name"$ib$BotDeathMatch(Level.Game).Maps[i]$ae);

	}
	SendLine("EMAP");
}


function ReceivedKick()
{
	local string target;
	local Controller C;
	local Bot b;

	target = GetArgVal("Id");
	if (target == "")
		return;
    for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( (C$C.PlayerReplicationInfo.PlayerID) == target )
		{
			if (C.isA('RemoteBot'))
			{
				//TODO: Send "Kicked"?
				RemoteBot(C).myConnection.SendLine("FIN");
				RemoteBot(C).myConnection.Closed();
			}
			else if (C.isA('Bot'))
			{
				b = Bot(C);

				//probably wont need this line with decreasing number of min players...
				UnrealMPGameInfo(Level.Game).MinPlayers = Max(UnrealMPGameInfo(Level.Game).MinPlayers - 1, UnrealMPGameInfo(Level.Game).NumPlayers + UnrealMPGameInfo(Level.Game).NumBots - 1);
				if ( (Vehicle(b.Pawn) != None) && (Vehicle(b.Pawn).Driver != None) )
					Vehicle(b.Pawn).Driver.KilledBy(Vehicle(b.Pawn).Driver);
				else if (b.Pawn != None)
					b.Pawn.KilledBy( b.Pawn );
				if (b != None)
				b.Destroy();
			}
			break;

		}

	}

}

function ReceivedPause()
{
	local bool bWasPaused;

	if (!bAllowPause)
		return;
	if ((Level.bPlayersOnly == true) || (Level.Pauser != none))
		bWasPaused = true;
	else
		bWasPaused = false;


	if (GetArgVal("PauseBots")!="")
	{
		Level.bPlayersOnly = bool(GetArgVal("PauseBots"));
	}
	if (GetArgVal("PauseAll")!="")
	{
		if (bool(GetArgVal("PauseAll")))
		{
			if (Level.Pauser == None)
			{
				Level.Pauser = BotDeathMatch(Level.Game).LevelPauserFeed;
			}
		}
		else
		{
			Level.Pauser = None;
		}
	}

	if (bWasPaused == true)
	{
		if ((Level.bPlayersOnly == false) && (Level.Pauser == none))
			SendNotifyPause(false); //send resume message
	}
	else
	{
		if ((Level.bPlayersOnly == true) || (Level.Pauser != none))
			SendNotifyPause(true); //send pause message
	}

}

function ReceivedPassword()
{
	local string target;

	target = GetArgVal("Password");

	if (target == BotDeathMatch(Level.Game).Password)
	{
		SendLine("PASSWDOK");
		SendGameInfo();
		gotoState('running','Waiting');
	}
	else
	{
		SendLine("PASSWDWRONG");
		Closed();
	}
}

function ReceivedReady()
{

	if ( IsInState('checkingPassword') )
		SendLine("PASSWORD"$ib$as$
			"BlockedByIP" $ib$ BotDeathMatch(Level.Game).PasswordByIP $ae);
	else
		SendGameInfo();

}

function ReceivedRec()
{
	local string target;

	target = GetArgVal("FileName");
	ConsoleCommand("demorec "$target, True);
	sendLine("RECSTART");
}

function ReceivedRespawn()
{
	local string target;
	local vector vector1;
	local rotator rotator1;
	local Controller C;

	target = GetArgVal("Id");

	if (target == "") {
		return;
	}
	ParseVector(vector1,"Location");
	ParseRot(rotator1,"Rotation");
	for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( (C$C.PlayerReplicationInfo.PlayerID) == target )
		{
			if (C.IsA('RemoteBot'))
				RemoteBot(C).RespawnPlayer(vector1,rotator1);
		    if (C.IsA('GBxPlayer'))
   				GBxPlayer(C).RespawnPlayer(vector1,rotator1);
		}

	}

}

function ReceivedSetGameSpeed()
{
	local float floatNumber;

	floatNumber = float(GetArgVal("Speed"));
	if ( (floatNumber >= 0.01) && (floatNumber <= 50 ) )
	{
		Level.Game.bAllowMPGameSpeed = true;
		Level.Game.SetGameSpeed(floatNumber);
		Level.Game.SaveConfig();
		Level.Game.GameReplicationInfo.SaveConfig();
	}
}

function ReceivedSetLock()
{
	local string target;

	target = GetArgVal("BotServer");
	if (target != "")
	{
		if (bool(target))
		{
			if (BotDeathMatch(Level.Game).theBotServer.LinkState == STATE_Listening)
			{
				BotDeathMatch(Level.Game).theBotServer.bClosed = true;
				BotDeathMatch(Level.Game).theBotServer.Close();
			}
		}
		else
		{
			if (BotDeathMatch(Level.Game).theBotServer.LinkState != STATE_Listening)
			{
				BotDeathMatch(Level.Game).theBotServer.bClosed = false;
				BotDeathMatch(Level.Game).theBotServer.Listen();
			}
		}
	}

	target = GetArgVal("ControlServer");
	if (target != "")
	{
		if (bool(target))
		{
			if (BotDeathMatch(Level.Game).theControlServer.LinkState == STATE_Listening)
			{
				BotDeathMatch(Level.Game).theControlServer.bClosed = true;
				BotDeathMatch(Level.Game).theControlServer.Close();
			}
		}
		else
		{
			if (BotDeathMatch(Level.Game).theControlServer.LinkState != STATE_Listening)
			{
				BotDeathMatch(Level.Game).theControlServer.bClosed = false;
				BotDeathMatch(Level.Game).theControlServer.Listen();
			}
		}
	}

}

function ReceivedSetPass()
{
	local string target;

	target = GetArgVal("Password");

	if (target != "")
	{
		BotDeathMatch(Level.Game).bPasswordProtected = true;
		BotDeathMatch(Level.Game).PasswordByIP = string(RemoteAddr.Addr)$":"$string(RemoteAddr.Port);
		BotDeathMatch(Level.Game).Password = target;
	}
	else
	{
		BotDeathMatch(Level.Game).bPasswordProtected = false;
		BotDeathMatch(Level.Game).PasswordByIP = "";
		BotDeathMatch(Level.Game).Password = "";
	}

}

function ReceivedSpawnActor()
{
	local string tmp;
	local vector v;
	local rotator r;

	ParseVector(v,"Location");
	ParseRot(r,"Rotation");
	tmp = GetArgVal("Type");
	if (tmp != "")
	{
		tmp = "class'"$tmp$"'";
		setPropertyText("tempActorClass",tmp);
		Spawn(tempActorClass,self,,v,r);
	}

}

function ReceivedStartPlrs()
{
	local string tmp;

	tmp = GetArgVal("Humans");
	if (tmp != "")
	{
		bExportHumanPlayers = bool(tmp);
	}
	tmp = GetArgVal("GBBots");
	if (tmp != "")
	{
		bExportRemoteBots = bool(tmp);
	}
	tmp = GetArgVal("UnrealBots");
	if (tmp != "")
	{
		bExportUnrealBots = bool(tmp);
	}
	gotoState('running','Running');
}

function ReceivedStopRec()
{
	ConsoleCommand("stopdemo", True);
	sendLine("RECEND");
}

function SendNotifyConf(RemoteBot theBot)
{
	local string outstring;

	outstring="CONFCH {Id " $ theBot $ theBot.PlayerReplicationInfo.PlayerID $
		"} {ManualSpawn " $ !theBot.bAutoSpawn $
		"} {AutoTrace " $ theBot.bAutoTrace $
		"} {Invulnerable " $ theBot.bGodMode $
		"} {Name " $ theBot.PlayerReplicationInfo.PlayerName $
		"} {VisionTime " $ theBot.myConnection.VisionTime $
		"} {ShowDebug " $ theBot.bDebug $
		"} {ShowFocalPoint " $ theBot.bShowFocalPoint $
		"} {DrawTraceLines " $ theBot.bDrawTraceLines $
		"} {SynchronousOff " $ theBot.myConnection.bSynchronousMessagesOff $
		"}";


	//Notify bot about his variables changing
	theBot.myConnection.SendLine(outstring);

	//Notify all control servers about bot variables chaning
	GlobalSendLine(outstring,true,false);
}

function SendNotifyPause ( bool bGamePaused )
{
	local Controller C;
	local GBClientClass G;

	for(C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( C.IsA('RemoteBot') )
		{
			if (bGamePaused)
				RemoteBot(C).myConnection.SendLine("PAUSED");
			else
				RemoteBot(C).myConnection.SendLine("RESUMED");
		}
	}

	for (G = Parent.ChildList; G != None; G = G.Next )
	{
		if (bGamePaused)
			G.SendLine("PAUSED");
		else
			G.SendLine("RESUMED");
	}

}

//from CheatManager class - for inspiration
/*
function Summon( string ClassName )
{
	local class<actor> NewClass;
	local vector SpawnLoc;

	//if (!areCheatsEnabled()) return;

	log( "Fabricate " $ ClassName );
	NewClass = class<actor>( DynamicLoadObject( ClassName, class'Class' ) );
	if( NewClass!=None )
	{
		if ( Pawn != None )
			SpawnLoc = Pawn.Location;
		else
			SpawnLoc = Location;
		Spawn( NewClass,,,SpawnLoc + 72 * Vector(Rotation) + vect(0,0,1) * 15 );
	}
	ReportCheat("Summon");
}
*/

//Default State for receiving commands
auto state running
{
Begin:
Waiting:
	sleep(1.0);
	SendLine("ALIVE {Time " $ Level.TimeSeconds $"}");
	goto 'Waiting';
Running:
	SendLine("ALIVE {Time " $ Level.TimeSeconds $"}");
	ExportPlayers();
	sleep(UpdateTime);
	goto 'Running';
}

state checkingPassword
{
Begin:
Waiting:
	sleep(5.0);
	goto 'Waiting';
}

defaultproperties
{
     UpdateTime=0.300000
}
